package com.dw.receiver.service.impl;

import com.dw.common.ThreadFactory;
import com.dw.common.bean.NetNode;
import com.dw.common.bean.ResourceBean;
import com.dw.common.configuration.Configuration;
import com.dw.common.mapper.ResourceMapper;
import com.dw.provider.ProviderGrpc;
import com.dw.receiver.service.ReceiverService;
import com.dw.receiver.strategy.NodeSelection;
import com.dw.receiver.strategy.ResourceSplit;
import com.dw.receiver.strategy.impl.NodeSelectionImpl;
import com.dw.receiver.strategy.impl.ResourceSplitImpl;
import com.dw.resource.GetResourceResponse;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ReceiverServiceImpl implements ReceiverService {
    public static List<ResourceBean> notRequired;
    private final BlockingDeque<GetResourceResponse> responses;
    private NodeSelection nodeSelection;

    public ReceiverServiceImpl() {
        responses = new LinkedBlockingDeque<>();
    }

    @Override
    public void getResource(ResourceBean resource) {
        List<NetNode> nodeList = nodeSelection.getNetNodeList();
        ResourceSplit resourceSplit = new ResourceSplitImpl(resource, nodeList.size());
        notRequired = resourceSplit.split();
        getResourceInternal(notRequired, resource);
        ScheduledExecutorService scheduledExecutorService = ThreadFactory.getScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                getResourceInternal(notRequired, resource);
            }
        }, 3, 3, TimeUnit.SECONDS);
    }

    private void getResourceInternal(List<ResourceBean> notRequired, ResourceBean resource) {
        nodeSelection = new NodeSelectionImpl(resource);
        List<NetNode> nodeList = nodeSelection.getNetNodeList();
        int length = nodeList.size();
        int index = 0;
        for (ResourceBean resourceBean : notRequired) {
            NetNode node;
            if (index < length) {
                node = nodeList.get(index);
                index++;
            } else {
                node = nodeList.get(0);
                index = 1;
            }
            ManagedChannel channel = ManagedChannelBuilder.forAddress(node.getIp(), node.getPort())
                    .usePlaintext()
                    .maxInboundMessageSize(Configuration.BATCH_SIZE)
                    .build();
            ProviderGrpc.ProviderFutureStub stub = ProviderGrpc.newFutureStub(channel);
            ListenableFuture<GetResourceResponse> response = stub.getResource(ResourceMapper.resourceBeanToResourceInfo(resourceBean));
            Futures.addCallback(response, new Task(), ThreadFactory.getExecutor());
        }
    }

    @Override
    public BlockingDeque<GetResourceResponse> getBlockingQueue() {
        return responses;
    }

    @Override
    public List<ResourceBean> getNotRequired() {
        return notRequired;
    }

    class Task implements FutureCallback<GetResourceResponse> {
        @Override
        public void onSuccess(@NullableDecl GetResourceResponse getResourceResponse) {
            try {
                if (getResourceResponse != null) {
                    responses.put(getResourceResponse);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}
